var example = example || {}; // namespace declaration
 
(function(global) { // wrapper-function head

	// private variable
	var pi = 3.141592;

	// public variable
	example.ModulePublicVariable = "This is module public variable!";

	/**
	 * Shape
	 */
	example.Shape = function (X, Y) {
		/*
		 * private properties	 
		 */ 
		var privatePrimitive = 123;
		var privateObject = { value:123 };
	
		/*
		 * public properties
		 */
		this.x = X;
		this.y = Y;
		
		/*
		 * public methods
		 */
		this.privatePrimitive = function () { return privatePrimitive; };  
		this.privateObject = function () { return privateObject; };
	};

	// object public method
	example.Shape.prototype.toString = function() {
		return "Shape@" + this.id + this.x + "," + this.y;
	};
	
	example.Shape.staticMethod = function () { return "This is static method!"; };

	/**
	 * Rectangle extends Shape
	 */
	example.Rectangle = function(X, Y, W, H) {
		example.Shape.call(this, X, Y); // ancestor constructor calling
		this.width = W;
		this.height = H;
	};
	
	example.Rectangle.prototype = new example.Shape(); // main inheritance declaration

	example.Rectangle.prototype.toString = function() {
		return "Rectangle" + "/" + example.Shape.prototype.toString.call(this) + "," + this.width + "," + this.height;
	};
	
	example.Rectangle.prototype.area = function() {
		return this.width * this.height;
	};
	
	/**
	 * Circle extends Shape
	 */
	example.Circle = function(X, Y, R) {
		example.Shape.call(this, X, Y); // ancestor constructor calling
		this.radius = R;
	};
	
	example.Circle.prototype = new example.Shape();

	// ancestor method overloading
	example.Circle.prototype.toString = function() {
		return "Circle" + "/" + example.Shape.prototype.toString.call(this) + "," + this.radius;
	};
	
	// successor new method
	example.Circle.prototype.area = function() {
		return pi * (this.radius * this.radius);
	};

	/**
	 * Ellipse extends Circle
	 */
	example.Ellipse = function(X, Y, R1, R2) {
		example.Circle.call(this, X, Y, R1);  // ancestor constructor calling
		this.radius1 = R1;
		this.radius2 = R2;
	};
	
	example.Ellipse.prototype = new example.Circle(); // main inheritance declaration
	
	example.Ellipse.prototype.toString = function() {
		return "Ellipse" + "/" + example.Shape.prototype.toString.call(this) + "," + this.radius1 + "," + this.radius1;
	};
	
	example.Ellipse.prototype.area = function() {
		return pi * (this.radius1 * this.radius2);
	};

} (this)); // wrapper-function tail and calling

/****************************************************************************/

console.log(example.ModulePublicVariable);
console.log(example.Shape.staticMethod());

var rectangle = new example.Rectangle(30, 40, 5, 23);
var circle = new example.Circle(20, 10, 5);
var ellipse = new example.Ellipse(20, 10, 5, 7);

objects = [rectangle, circle, ellipse];
for (i in objects) {
	console.log("Area of", objects[i].toString(), "=", objects[i].area());
}

// Private property as primitive is read-only and can't be changed.
privatePrimitive = rectangle.privatePrimitive();
privatePrimitive = 456;
console.log(privatePrimitive + " != " + rectangle.privatePrimitive());

// Private property as object can be changed!
privateObject = rectangle.privateObject();
privateObject.value = 456;
console.log(privateObject.value + " == " + rectangle.privateObject().value);